/*
 * @(#)DefaultTreeCellRenderer.java	1.62 08/02/04
 *
 * Copyright 2006 Sun Microsystems, Inc. All rights reserved.
 * SUN PROPRIETARY/CONFIDENTIAL. Use is subject to license terms.
 */
package hextd.UI;

import com.usoog.tdcore.UserInfo;
import com.usoog.tdcore.map.MapInfo;
import java.awt.Color;
import java.awt.Component;
import java.awt.Dimension;
import java.awt.Font;
import java.awt.Graphics;
import java.awt.Insets;
import java.awt.Rectangle;
import javax.swing.plaf.ColorUIResource;
import javax.swing.plaf.FontUIResource;
import javax.swing.plaf.basic.BasicGraphicsUtils;
import javax.swing.Icon;
import javax.swing.JLabel;
import javax.swing.JTree;
import javax.swing.LookAndFeel;
import javax.swing.UIManager;
import javax.swing.border.EmptyBorder;
import javax.swing.tree.DefaultMutableTreeNode;
import javax.swing.tree.DefaultTreeCellRenderer;
import javax.swing.tree.TreeCellRenderer;

/**
 * Copy of DefaultTreeCellRenderer modified to paint map names, including
 * inactive maps.
 *
 * Displays an entry in a tree.
 * <code>DefaultTreeCellRenderer</code> is not opaque and
 * unless you subclass paint you should not change this.
 * See <a
href="http://java.sun.com/docs/books/tutorial/uiswing/components/tree.html">How to Use Trees</a>
 * in <em>The Java Tutorial</em>
 * for examples of customizing node display using this class.
 * <p>
 *
 * <strong><a name="override">Implementation Note:</a></strong>
 * This class overrides
 * <code>invalidate</code>,
 * <code>validate</code>,
 * <code>revalidate</code>,
 * <code>repaint</code>,
 * and
 * <code>firePropertyChange</code>
 * solely to improve performance.
 * If not overridden, these frequently called methods would execute code paths
 * that are unnecessary for the default tree cell renderer.
 * If you write your own renderer,
 * take care to weigh the benefits and
 * drawbacks of overriding these methods.
 *
 * <p>
 * <strong>Warning:</strong>
 * Serialized objects of this class will not be compatible with
 * future Swing releases. The current serialization support is
 * appropriate for short term storage or RMI between applications running
 * the same version of Swing.  As of 1.4, support for long term storage
 * of all JavaBeans<sup><font size="-2">TM</font></sup>
 * has been added to the <code>java.beans</code> package.
 * Please see {@link java.beans.XMLEncoder}.
 *
 * @version 1.62 02/04/08
 * @author Rob Davis
 * @author Ray Ryan
 * @author Scott Violet
 */
public class MapListTreeCellRenderer extends JLabel implements TreeCellRenderer {

	/** Last tree the renderer was painted in. */
	private JTree tree;
	/** Is the value currently selected. */
	protected boolean selected;
	/** True if has focus. */
	protected boolean hasFocus;
	/** True if draws focus border around icon as well. */
	private boolean drawsFocusBorderAroundIcon;
	/** If true, a dashed line is drawn as the focus indicator. */
	private boolean drawDashedFocusIndicator;
	// If drawDashedFocusIndicator is true, the following are used.
	/**
	 * Background color of the tree.
	 */
	private Color treeBGColor;
	/**
	 * Color to draw the focus indicator in, determined from the background.
	 * color.
	 */
	private Color focusBGColor;
	// Icons
	/** Icon used to show non-leaf nodes that aren't expanded. */
	transient protected Icon closedIcon;
	/** Icon used to show leaf nodes. */
	transient protected Icon leafIcon;
	/** Icon used to show non-leaf nodes that are expanded. */
	transient protected Icon openIcon;
	// Colors
	/** Color to use for the foreground for selected nodes. */
	protected Color textSelectionColor;
	/** Color to use for the foreground for non-selected nodes. */
	protected Color textNonSelectionColor;
	/** Color to use for the foreground for disabled nodes. */
	private Color textDisabledColor;
	/** Color to use for the background when a node is selected. */
	protected Color backgroundSelectionColor;
	/** Color to use for the background when the node isn't selected. */
	protected Color backgroundNonSelectionColor;
	/** Color to use for the focus indicator when the node has focus. */
	protected Color borderSelectionColor;
	private boolean isDropCell;
	private boolean fillBackground = true;
	private UserInfo userInfo;

	/**
	 * Returns a new instance of DefaultTreeCellRenderer.  Alignment is
	 * set to left aligned. Icons and text color are determined from the
	 * UIManager.
	 */
	public MapListTreeCellRenderer(UserInfo localUserInfo) {
		userInfo = localUserInfo;
		DefaultTreeCellRenderer temp = new DefaultTreeCellRenderer();
		setLeafIcon(temp.getLeafIcon());
		setClosedIcon(temp.getClosedIcon());
		setOpenIcon(temp.getOpenIcon());

		setTextSelectionColor(temp.getTextSelectionColor());
		setTextNonSelectionColor(temp.getTextNonSelectionColor());
		textDisabledColor = textNonSelectionColor;
		setBackgroundSelectionColor(temp.getBackgroundSelectionColor());
		setBackgroundNonSelectionColor(temp.getBackgroundNonSelectionColor());
		setBorderSelectionColor(temp.getBorderSelectionColor());
		drawsFocusBorderAroundIcon = false;
		drawDashedFocusIndicator = false;

		fillBackground = true;

		Insets margins = temp.getInsets();
		if (margins != null) {
			setBorder(new EmptyBorder(margins.top, margins.left,
					margins.bottom, margins.right));
		}

		setName("Tree.cellRenderer");
	}

	/**
	 * Sets the icon used to represent non-leaf nodes that are expanded.
	 */
	public void setOpenIcon(Icon newIcon) {
		openIcon = newIcon;
	}

	/**
	 * Returns the icon used to represent non-leaf nodes that are expanded.
	 */
	public Icon getOpenIcon() {
		return openIcon;
	}

	/**
	 * Sets the icon used to represent non-leaf nodes that are not expanded.
	 */
	public void setClosedIcon(Icon newIcon) {
		closedIcon = newIcon;
	}

	/**
	 * Returns the icon used to represent non-leaf nodes that are not
	 * expanded.
	 */
	public Icon getClosedIcon() {
		return closedIcon;
	}

	/**
	 * Sets the icon used to represent leaf nodes.
	 */
	public void setLeafIcon(Icon newIcon) {
		leafIcon = newIcon;
	}

	/**
	 * Returns the icon used to represent leaf nodes.
	 */
	public Icon getLeafIcon() {
		return leafIcon;
	}

	/**
	 * Sets the color the text is drawn with when the node is selected.
	 */
	public void setTextSelectionColor(Color newColor) {
		textSelectionColor = newColor;
	}

	/**
	 * Returns the color the text is drawn with when the node is selected.
	 */
	public Color getTextSelectionColor() {
		return textSelectionColor;
	}

	/**
	 * Sets the color the text is drawn with when the node isn't selected.
	 */
	public void setTextNonSelectionColor(Color newColor) {
		textNonSelectionColor = newColor;
	}

	/**
	 * Returns the color the text is drawn with when the node isn't selected.
	 */
	public Color getTextNonSelectionColor() {
		return textNonSelectionColor;
	}

	/**
	 * Sets the color to use for the background if node is selected.
	 */
	public void setBackgroundSelectionColor(Color newColor) {
		backgroundSelectionColor = newColor;
	}

	/**
	 * Returns the color to use for the background if node is selected.
	 */
	public Color getBackgroundSelectionColor() {
		return backgroundSelectionColor;
	}

	/**
	 * Sets the background color to be used for non selected nodes.
	 */
	public void setBackgroundNonSelectionColor(Color newColor) {
		backgroundNonSelectionColor = newColor;
	}

	/**
	 * Returns the background color to be used for non selected nodes.
	 */
	public Color getBackgroundNonSelectionColor() {
		return backgroundNonSelectionColor;
	}

	/**
	 * Sets the color to use for the border.
	 */
	public void setBorderSelectionColor(Color newColor) {
		borderSelectionColor = newColor;
	}

	/**
	 * Returns the color the border is drawn.
	 */
	public Color getBorderSelectionColor() {
		return borderSelectionColor;
	}

	/**
	 * Subclassed to map <code>FontUIResource</code>s to null. If
	 * <code>font</code> is null, or a <code>FontUIResource</code>, this
	 * has the effect of letting the font of the JTree show
	 * through. On the other hand, if <code>font</code> is non-null, and not
	 * a <code>FontUIResource</code>, the font becomes <code>font</code>.
	 */
	@Override
	public void setFont(Font font) {
		if (font instanceof FontUIResource) {
			font = null;
		}
		super.setFont(font);
	}

	/**
	 * Gets the font of this component.
	 * @return this component's font; if a font has not been set
	 * for this component, the font of its parent is returned
	 */
	@Override
	public Font getFont() {
		Font font = super.getFont();

		if (font == null && tree != null) {
			// Strive to return a non-null value, otherwise the html support
			// will typically pick up the wrong font in certain situations.
			font = tree.getFont();
		}
		return font;
	}

	/**
	 * Subclassed to map <code>ColorUIResource</code>s to null. If
	 * <code>color</code> is null, or a <code>ColorUIResource</code>, this
	 * has the effect of letting the background color of the JTree show
	 * through. On the other hand, if <code>color</code> is non-null, and not
	 * a <code>ColorUIResource</code>, the background becomes
	 * <code>color</code>.
	 */
	@Override
	public void setBackground(Color color) {
		if (color instanceof ColorUIResource) {
			color = null;
		}
		super.setBackground(color);
	}

	private boolean havePreReq(UserInfo ui, MapInfo map) {
		for (Integer i : map.preReq) {
			if (!ui.completedMaps.contains(i)) {
				return false;
			}
		}
		return true;
	}

	/**
	 * Configures the renderer based on the passed in components.
	 * The value is set from messaging the tree with
	 * <code>convertValueToText</code>, which ultimately invokes
	 * <code>toString</code> on <code>value</code>.
	 * The foreground color is set based on the selection and the icon
	 * is set based on the <code>leaf</code> and <code>expanded</code>
	 * parameters.
	 */
	@Override
	public Component getTreeCellRendererComponent(JTree tree, Object value,
			boolean sel,
			boolean expanded,
			boolean leaf, int row,
			boolean hasFocus) {
		boolean playable = true;

		if (value instanceof DefaultMutableTreeNode) {
			Object uo = ((DefaultMutableTreeNode) value).getUserObject();

			if (uo instanceof MapInfo) {
				MapInfo map = (MapInfo) uo;
				if (!havePreReq(userInfo, map)) {
					playable = false;
				}
			}
		}

		String stringValue = tree.convertValueToText(
				value, sel, expanded, leaf, row, hasFocus);

		this.tree = tree;
		this.hasFocus = hasFocus;
		setText(stringValue);

		Color fg = null;
		isDropCell = false;

		JTree.DropLocation dropLocation = tree.getDropLocation();
		if (dropLocation != null
				&& dropLocation.getChildIndex() == -1
				&& tree.getRowForPath(dropLocation.getPath()) == row) {

			fg = getTextSelectionColor();

			isDropCell = true;
		} else if (!playable) {
			fg = textDisabledColor;
		} else if (sel) {
			fg = getTextSelectionColor();
		} else {
			fg = getTextNonSelectionColor();
		}

		setForeground(fg);

		Icon icon = null;
		if (leaf) {
			icon = getLeafIcon();
		} else if (expanded) {
			icon = getOpenIcon();
		} else {
			icon = getClosedIcon();
		}

		if (!tree.isEnabled()) {
			setEnabled(false);
			LookAndFeel laf = UIManager.getLookAndFeel();
			Icon disabledIcon = laf.getDisabledIcon(tree, icon);
			if (disabledIcon != null) {
				icon = disabledIcon;
			}
			setDisabledIcon(icon);
		} else {
			setEnabled(true);
			setIcon(icon);
		}
		setComponentOrientation(tree.getComponentOrientation());

		selected = sel;

		return this;
	}

	/**
	 * Paints the value.  The background is filled based on selected.
	 */
	@Override
	public void paint(Graphics g) {
		Color bColor = null;

		if (isDropCell) {
			//bColor = DefaultLookup.getColor(this, ui, "Tree.dropCellBackground");
			if (bColor == null) {
				bColor = getBackgroundSelectionColor();
			}
		} else if (selected) {
			bColor = getBackgroundSelectionColor();
		} else {
			bColor = getBackgroundNonSelectionColor();
			if (bColor == null) {
				bColor = getBackground();
			}
		}

		int imageOffset = -1;
		if (bColor != null && fillBackground) {
			Icon currentI = getIcon();

			imageOffset = getLabelStart();
			g.setColor(bColor);
			if (getComponentOrientation().isLeftToRight()) {
				g.fillRect(imageOffset, 0, getWidth() - imageOffset,
						getHeight());
			} else {
				g.fillRect(0, 0, getWidth() - imageOffset,
						getHeight());
			}
		}

		if (hasFocus) {
			if (drawsFocusBorderAroundIcon) {
				imageOffset = 0;
			} else if (imageOffset == -1) {
				imageOffset = getLabelStart();
			}
			if (getComponentOrientation().isLeftToRight()) {
				paintFocus(g, imageOffset, 0, getWidth() - imageOffset,
						getHeight(), bColor);
			} else {
				paintFocus(g, 0, 0, getWidth() - imageOffset, getHeight(), bColor);
			}
		}
		super.paint(g);
	}

	private void paintFocus(Graphics g, int x, int y, int w, int h, Color notColor) {
		Color bsColor = getBorderSelectionColor();

		if (bsColor != null && (selected || !drawDashedFocusIndicator)) {
			g.setColor(bsColor);
			g.drawRect(x, y, w - 1, h - 1);
		}
		if (drawDashedFocusIndicator && notColor != null) {
			if (treeBGColor != notColor) {
				treeBGColor = notColor;
				focusBGColor = new Color(~notColor.getRGB());
			}
			g.setColor(focusBGColor);
			BasicGraphicsUtils.drawDashedRect(g, x, y, w, h);
		}
	}

	private int getLabelStart() {
		Icon currentI = getIcon();
		if (currentI != null && getText() != null) {
			return currentI.getIconWidth() + Math.max(0, getIconTextGap() - 1);
		}
		return 0;
	}

	/**
	 * Overrides <code>JComponent.getPreferredSize</code> to
	 * return slightly wider preferred size value.
	 */
	@Override
	public Dimension getPreferredSize() {
		Dimension retDimension = super.getPreferredSize();

		if (retDimension != null) {
			retDimension = new Dimension(retDimension.width + 3,
					retDimension.height);
		}
		return retDimension;
	}

	/**
	 * Overridden for performance reasons.
	 * See the <a href="#override">Implementation Note</a>
	 * for more information.
	 */
	@Override
	public void validate() {
	}

	/**
	 * Overridden for performance reasons.
	 * See the <a href="#override">Implementation Note</a>
	 * for more information.
	 *
	 * @since 1.5
	 */
	@Override
	public void invalidate() {
	}

	/**
	 * Overridden for performance reasons.
	 * See the <a href="#override">Implementation Note</a>
	 * for more information.
	 */
	@Override
	public void revalidate() {
	}

	/**
	 * Overridden for performance reasons.
	 * See the <a href="#override">Implementation Note</a>
	 * for more information.
	 */
	@Override
	public void repaint(long tm, int x, int y, int width, int height) {
	}

	/**
	 * Overridden for performance reasons.
	 * See the <a href="#override">Implementation Note</a>
	 * for more information.
	 */
	@Override
	public void repaint(Rectangle r) {
	}

	/**
	 * Overridden for performance reasons.
	 * See the <a href="#override">Implementation Note</a>
	 * for more information.
	 *
	 * @since 1.5
	 */
	@Override
	public void repaint() {
	}

	/**
	 * Overridden for performance reasons.
	 * See the <a href="#override">Implementation Note</a>
	 * for more information.
	 */
	@Override
	protected void firePropertyChange(String propertyName, Object oldValue, Object newValue) {
		// Strings get interned...
		if (propertyName.equals("text")
				|| ((propertyName.equals("font")
				|| propertyName.equals("foreground"))
				&& oldValue != newValue
				&& getClientProperty(javax.swing.plaf.basic.BasicHTML.propertyKey) != null)) {

			super.firePropertyChange(propertyName, oldValue, newValue);
		}
	}

	/**
	 * Overridden for performance reasons.
	 * See the <a href="#override">Implementation Note</a>
	 * for more information.
	 */
	@Override
	public void firePropertyChange(String propertyName, byte oldValue, byte newValue) {
	}

	/**
	 * Overridden for performance reasons.
	 * See the <a href="#override">Implementation Note</a>
	 * for more information.
	 */
	@Override
	public void firePropertyChange(String propertyName, char oldValue, char newValue) {
	}

	/**
	 * Overridden for performance reasons.
	 * See the <a href="#override">Implementation Note</a>
	 * for more information.
	 */
	@Override
	public void firePropertyChange(String propertyName, short oldValue, short newValue) {
	}

	/**
	 * Overridden for performance reasons.
	 * See the <a href="#override">Implementation Note</a>
	 * for more information.
	 */
	@Override
	public void firePropertyChange(String propertyName, int oldValue, int newValue) {
	}

	/**
	 * Overridden for performance reasons.
	 * See the <a href="#override">Implementation Note</a>
	 * for more information.
	 */
	@Override
	public void firePropertyChange(String propertyName, long oldValue, long newValue) {
	}

	/**
	 * Overridden for performance reasons.
	 * See the <a href="#override">Implementation Note</a>
	 * for more information.
	 */
	@Override
	public void firePropertyChange(String propertyName, float oldValue, float newValue) {
	}

	/**
	 * Overridden for performance reasons.
	 * See the <a href="#override">Implementation Note</a>
	 * for more information.
	 */
	@Override
	public void firePropertyChange(String propertyName, double oldValue, double newValue) {
	}

	/**
	 * Overridden for performance reasons.
	 * See the <a href="#override">Implementation Note</a>
	 * for more information.
	 */
	@Override
	public void firePropertyChange(String propertyName, boolean oldValue, boolean newValue) {
	}

	public Color getTextDisabledColor() {
		return textDisabledColor;
	}

	public void setTextDisabledColor(Color textDisabledColor) {
		this.textDisabledColor = textDisabledColor;
	}
}
